package com.cskaoyan.wordmemorize.common.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ConverterUtils;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cskaoyan.wordmemorize.common.constant.ResultCodeEnum;
import com.cskaoyan.wordmemorize.common.exception.BusinessException;
import com.cskaoyan.wordmemorize.common.exception.ExcelParseException;
import com.cskaoyan.wordmemorize.converter.TopicConverter;
import com.cskaoyan.wordmemorize.dao.entity.ReviewTopicDO;
import com.cskaoyan.wordmemorize.dao.entity.TopicOptionDO;
import com.cskaoyan.wordmemorize.dao.entity.VocDO;
import com.cskaoyan.wordmemorize.dao.mapper.VocMapper;
import com.cskaoyan.wordmemorize.dto.admin.SingleTopicExcelDTO;
import com.cskaoyan.wordmemorize.service.ReviewTopicOptionService;
import com.cskaoyan.wordmemorize.service.ReviewTopicService;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// 有个很重要的点 DemoDataListener 不能被spring管理，要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class SingleTopicExcelCheckListener implements ReadListener<SingleTopicExcelDTO> {

    /**
     * 每隔5条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List<SingleTopicExcelDTO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    /**
     * 假设这个是一个Mapper，当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */

    private VocMapper vocMapper;


    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param vocMapper
     */
    public SingleTopicExcelCheckListener(VocMapper vocMapper) {
        this.vocMapper = vocMapper;

    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(SingleTopicExcelDTO data, AnalysisContext context) {

        if ( (data.getTopic() == null) || (data.getOption1() == null) || (data.getOption2() == null)
            || (data.getOption3() == null) || (data.getOption4() == null) || (data.getAnswerIndex() == null)){
            throw new ExcelParseException(ResultCodeEnum.VOC_EXCEL_DATA_ERROR,"单词属性不能有null");
        }

        if ( checkWord(data.getOption1()) || checkWord(data.getOption2())
            || checkWord(data.getOption3()) || checkWord(data.getOption4())){
            throw new ExcelParseException(ResultCodeEnum.VOC_EXCEL_PARSE_ERROR,ResultCodeEnum.VOC_EXCEL_PARSE_ERROR.getMessage());
        }

        Integer answerIndex = data.getAnswerIndex();
        if ( (answerIndex < 1) || (answerIndex > 4)){
            throw new ExcelParseException(ResultCodeEnum.PARAM_ERROR,"答案参数非法");
        }

    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        log.info("所有数据解析完成！");
    }

    /**
     *  用来读取数据头
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
    }

    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        int size = headMap.size();
        if (size != 6)
        {
            throw new ExcelParseException(ResultCodeEnum.VOC_EXCEL_FORMAT_ERROR,"Excel文件头与模版文件的文件头数量不一致");
        }
    }

    /**
     * 1. 校验单词是否存在
     * 2. 存在 返回false ，不存在 返回ture
     * @param word 被校验的单词
     * @return
     */
    public boolean checkWord(String word) {
        LambdaQueryWrapper<VocDO> vocDOWrapper = new LambdaQueryWrapper<>();
        vocDOWrapper.eq(VocDO::getContent, word);
        VocDO vocDO = vocMapper.selectOne(vocDOWrapper);
        if (vocDO == null) {
            return true;
        }

        return false;
    }
}